using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Drawing.Drawing2D;
using System.Drawing;
using MacomberMap.Common.Components;
using MacomberMap.Common.Internals;
using MacomberMap.Common.Logging;
using MacomberMap.Common.User_Interfaces.Components;
using MacomberMap.Common.Types;
using System.ComponentModel;
using MacomberMap.Common.Convertors;
using System.Reflection;
using MacomberMap.Common.Database_Interface;
using MacomberMap.Common.Events;

namespace MacomberMap.Common.User_Interfaces.One_Lines.Elements
{
    /// <summary>
    /// This class holds information on an element that can be presented to the user
    /// </summary>
    public abstract class MM_OneLine_Element : UserControl, MM_OneLine_Linked
    {
        /// <summary>
        /// The center of the element
        /// </summary>
        [Category("Layout"),Description("The center of the element")]
        public Point Center
        {
            get { return CenterRect(this.Bounds); }
            set
            {
                Point Offset = CenterRect(this.DisplayRectangle);
                this.Location = new Point(value.X - Offset.X, value.Y - Offset.Y);
            }
        }


        #region Enumerations
        /// <summary>The possible orientations for attributes of the element</summary>
        public enum enumOrientations
        {
            /// <summary>Unknown orientation</summary>
            Unknown,            
            /// <summary>Upward orientation</summary>
            Up,
            /// <summary>Downward orientation</summary>
            Down,
            /// <summary>Leftward orientation</summary>
            Left,
            /// <summary>Rightward orientation</summary>
            Right,
            /// <summary>Horizontal orientation</summary>
            Horizontal,
            /// <summary>Vertical orientation</summary>
            Vertical                    
        };

        /// <summary>
        /// Determine whether the element is vertical
        /// </summary>
        public bool IsVertical
        {
            get { return Orientation == MM_OneLine_Element.enumOrientations.Unknown || Orientation == MM_OneLine_Element.enumOrientations.Vertical || Orientation == MM_OneLine_Element.enumOrientations.Up || Orientation == MM_OneLine_Element.enumOrientations.Down; }
        }

        /// <summary>The types of elements that can be displayed on a one-line</summary>
        public enum ElementTypeEnum
        {
            /// <summary></summary>
            Unknown,
            /// <summary></summary>
            Line,
            /// <summary></summary>
            Breaker,
            /// <summary></summary>
            Switch,
            /// <summary></summary>
            Unit,
            /// <summary></summary>
            Load,
            /// <summary></summary>
            Transformer,
            /// <summary></summary>
            PricingVector,
            /// <summary></summary>
            Bus,
            /// <summary></summary>
            Capacitor,
            /// <summary></summary>
            Reactor,
            /// <summary></summary>
            StaticVarCompensator
        }
        #endregion

        #region Variable declarations

        /// <summary>The display parameters associated with the one-line</summary>
        public MM_OneLine_Display DisplayParameters;


        #region Data linkages
        ///<summary>The TEID of the element</summary>
        [Category("Data Linkages"), Description("The TEID of the element")]
        public UInt64 TEID
        {
            get
            {
                if (BaseElement == null)
                    return 0;
                else
                    return BaseElement.TEID;
            }
        }

        /// <summary>The rdf:ID of the element</summary>
        [Category("Data Linkages"),Description("The rdf:ID of the element")]
        public MM_rdfID rdfID
        {
            get { return _rdfID; }
            set { _rdfID = value; }
        }
        private MM_rdfID _rdfID;

        
        /// <summary>The MM element upon which this element is based</summary>
        [Category("Data Linkages"), Description("The MM element upon which this element is based")]
        public MM_Element BaseElement
        {
            get { return _BaseElement; }
            set { _BaseElement = value;  this.Invalidate(); }
        }
        private MM_Element _BaseElement = null;

        /// <summary>The KV level of an element, if any</summary>        
        [Category("Data Linkages"), Description("The KV level of an element, if any"), TypeConverter(typeof(MM_KVLevel_Locator))]
        public MM_KVLevel KVLevel
        {
            get
            {
                if (_KVLevel != null)
                    return _KVLevel;
                else if (BaseElement != null)
                    return BaseElement.KVLevel;
                else
                    return null;
            }
            set { _KVLevel = value; }
        }
        private MM_KVLevel _KVLevel;
        #endregion

        #region Display parameters
        /// <summary>
        /// Whether an arrow should be drawn from descriptor to this element
        /// </summary>
        [Category("Display"), Description("Whether an arrow should be drawn from descriptor to this element"), DefaultValue(false)]
        public bool DescriptorArrow
        {
            get { return _DescriptorArrow; }
            set { _DescriptorArrow = value; }
        }
        private bool _DescriptorArrow = false;


        /// <summary>The descriptor label associated with the element</summary>
        [Category("Display"), Description("The descriptor label associated with the element")]
        public MM_OneLine_Descriptor Descriptor
        {
            get { return _Descriptor; }
            set
            {
                _Descriptor = value; if (Parent != null)
                    Parent.Refresh();
            }
        }
        private MM_OneLine_Descriptor _Descriptor;

        /// <summary>The descriptor label associated with the element</summary>
        [Category("Display"), Description("The secondary descriptor associated with the element")]
        public MM_OneLine_SecondaryDescriptor SecondaryDescriptor
        {
            get { return _SecondaryDescriptor; }
            set
            {
                _SecondaryDescriptor  = value; if (Parent != null)
                    Parent.Refresh();
            }
        }
        private MM_OneLine_SecondaryDescriptor _SecondaryDescriptor;

        /// <summary>
        /// The inverted orientation of the element
        /// </summary>
        [Category("Display"), Description("The inverted orientation of the breaker")]
        public enumOrientations InvertedOrientation
        {
            get
            {
                if (_Orientation == enumOrientations.Vertical || _Orientation == enumOrientations.Horizontal || _Orientation == enumOrientations.Unknown)
                    return _Orientation;
                else if (_Orientation == enumOrientations.Left)
                    return enumOrientations.Right;
                else if (_Orientation == enumOrientations.Right)
                    return enumOrientations.Left;
                else if (_Orientation == enumOrientations.Up)
                    return enumOrientations.Down;
                else if (_Orientation == enumOrientations.Down)
                    return enumOrientations.Up;
                else
                    throw new InvalidOperationException("Unknown orientation: " + _Orientation.ToString());
            }
        }


        /// <summary>
        /// The orientation of the element
        /// </summary>
        [Category("Display"), Description("The orientation of the breaker")]
        public enumOrientations Orientation
        {
            get { return _Orientation; }
            set
            {
                _Orientation = value;

                //Now, update the size of the element according to its type and size                
                if (this is MM_OneLine_Breaker)
                    UpdateSize(26, 26, enumOrientations.Horizontal);
                else if (this is MM_OneLine_Switch)
                    UpdateSize(26, 26, enumOrientations.Horizontal);
                else if (this is MM_OneLine_Unit)
                    UpdateSize(26, 26, enumOrientations.Left);
                else if (this is MM_OneLine_Transformer)
                {
                    MM_OneLine_Transformer XF = this as MM_OneLine_Transformer;
                    if (XF.Windings.Length == 1)
                        UpdateSize(13, 25, enumOrientations.Horizontal);
                    else
                        UpdateSize(26, 25, enumOrientations.Horizontal);

                    Point CenterXF = CenterRect(XF.DisplayRectangle);
                    for (int a = 0; a < XF.Windings.Length; a++)
                        if (XF.Windings[a] != null)
                            if (XF.Orientation == enumOrientations.Up || XF.Orientation == enumOrientations.Down || XF.Orientation == enumOrientations.Vertical)
                            {
                                XF.Windings[a].Bounds = new Rectangle(0, (a == 0 ? 0 : CenterXF.Y), XF.Width, CenterXF.Y);
                                XF.Windings[a].Orientation = a == 0 ? enumOrientations.Up : enumOrientations.Down;
                            }
                            else
                            {
                                XF.Windings[a].Bounds = new Rectangle((a == 0 ? 0 : CenterXF.X), 0, CenterXF.X, XF.Height);
                                XF.Windings[a].Orientation = a == 1 ? enumOrientations.Left : enumOrientations.Right;
                            }
                }
                else if (this is MM_OneLine_PricingVector)
                    UpdateSize(30, 15, enumOrientations.Left);
                else if (this is MM_OneLine_PokePoint && (this as MM_OneLine_PokePoint).IsJumper)
                    UpdateSize(35, 11, enumOrientations.Left);
                else if (this is MM_OneLine_Load || this is MM_OneLine_LAAR || this is MM_OneLine_Line)
                    UpdateSize(23, 15, enumOrientations.Left);
                else if (this is MM_OneLine_Reactor)
                    UpdateSize(32, 25, enumOrientations.Horizontal);
                else if (this is MM_OneLine_Capacitor)
                    UpdateSize(24, 20, enumOrientations.Left);
                else if (this is MM_OneLine_EndCap)
                    UpdateSize(7, 7, enumOrientations.Left);
                else if (this is MM_OneLine_StaticVarCompensator)
                    UpdateSize(70, 77, enumOrientations.Left);
                
            }
        }
        private enumOrientations _Orientation;
        #endregion

        #region Formatting assistance
        /// <summary>
        /// Update the size of the element based on its orientation
        /// </summary>
        /// <param name="Width">The width of the element if it's horizontal</param>
        /// <param name="Height">The height of the element if it's horizontal</param>
        /// <param name="DefaultOrientation">The default orientation</param>
        public void UpdateSize(int Width, int Height, enumOrientations DefaultOrientation)
        {
            if (Orientation == enumOrientations.Unknown)            
                Orientation = DefaultOrientation;                
            else if (Orientation == enumOrientations.Up || Orientation == enumOrientations.Down || Orientation == enumOrientations.Vertical)
                this.Size = new Size(Height, Width);
            else
                this.Size = new Size(Width,Height);
        }
  


        /// <summary>
        /// Return the center point of a rectangle
        /// </summary>
        /// <param name="Rect"></param>
        /// <returns></returns>
        public static Point CenterRect(Rectangle Rect)
        {
            return new Point(Rect.Left + (Rect.Width / 2), Rect.Top +  (Rect.Height/2));            
        }

        /// <summary>The string format that makes text centered</summary>
        public static StringFormat CenterFormat
        {
            get
            {
                if (_CenterFormat == null)
                {
                    _CenterFormat = new StringFormat();
                    _CenterFormat.Alignment = _CenterFormat.LineAlignment = StringAlignment.Center;
                }
                return _CenterFormat;
            }
        }
        private static StringFormat _CenterFormat;
        #endregion

        /// <summary>Whether the element is found within CIM</summary>
        public bool NotInCIM = false;

        /// <summary>Whether the element is found within the one-line</summary>
        public bool NotInOneLine = false;
        #endregion

        #region Initialization
        /// <summary>
        /// Initialize a blank one-line element
        /// </summary>
        /// <param name="DisplayParameters"></param>
        public MM_OneLine_Element(MM_OneLine_Display DisplayParameters)
        {
            this.DisplayParameters = DisplayParameters;
            this.AllowDrop = true;
        }




        /// <summary>
        /// Initialize a new one-line element from a one-line XML.
        /// </summary>
        /// <param name="ElemConfig">The one line's XML</param>
        /// <param name="DisplayParameters">The display parameters for the one-line</param>
        /// <param name="DbConnection">The database connection for locating base elements</param>
        public MM_OneLine_Element(XmlElement ElemConfig, MM_OneLine_Display DisplayParameters, MM_Database DbConnection)
        {            
            MM_Serializable.ReadXml(ElemConfig, this);
            this.DisplayParameters = DisplayParameters;
            if (ElemConfig.HasAttribute("TEID") && MM_Repository.TEIDs.ContainsKey(XmlConvert.ToUInt64(ElemConfig.Attributes["TEID"].Value)))
                BaseElement = MM_Repository.TEIDs[XmlConvert.ToUInt64(ElemConfig.Attributes["TEID"].Value)];            
            else if (ElemConfig.HasAttribute("TEID"))
                //If we don't have the requested CIM element, we need to retrieve it      
                if ((BaseElement = DbConnection.LocateElement(UInt64.Parse(ElemConfig.Attributes["TEID"].Value))) == null)
                    MM_Log.LogEvent(new MM_Event_ElementNotFound(UInt64.Parse(ElemConfig.Attributes["TEID"].Value), ElemConfig.Attributes["Name"].Value, MM_Overall_Display.ElemTypes[ElemConfig.Name]));
            if (ElemConfig.HasAttribute("Bounds"))
            {
                string[] splStr = ElemConfig.Attributes["Bounds"].Value.Split(',');
                this.Location = new Point(int.Parse(splStr[0]), int.Parse(splStr[1]));
                this.Size = new Size(int.Parse(splStr[2]), int.Parse(splStr[3]));                
            }

            //If we have a descriptor, roll it in.
            if (ElemConfig["Descriptor"] != null)
            {
                Descriptor = new MM_OneLine_Descriptor(this, ElemConfig["Descriptor"], DisplayParameters);
                Descriptor.RecomputeSize();
            }
            else if (ElemConfig["SecondaryDescriptor"] != null)
            {
                SecondaryDescriptor = new MM_OneLine_SecondaryDescriptor(this, ElemConfig["SecondaryDescriptor"], DisplayParameters);
                SecondaryDescriptor.RecomputeSize();
            }
            this.AllowDrop = true;

            
        }

        /// <summary>
        /// Initialize a new one-line element from a CIM element
        /// </summary>
        /// <param name="BaseElement">The element upon which the one-line element is based</param>
        /// <param name="DisplayParameters">The display parameters for the one-line</param>
        /// <param name="Orientation">The orientation of the element</param>
        public MM_OneLine_Element(MM_Element BaseElement, MM_OneLine_Display DisplayParameters, enumOrientations Orientation)
        {
            this.DisplayParameters = DisplayParameters;
            this.Orientation = Orientation;
            this.BaseElement = BaseElement;
            if (BaseElement != null)
                this.Name = BaseElement.Name;
            this.AllowDrop = true;

        }     
        #endregion

        #region Orientation parsing
        /// <summary>
        /// Parse the orientation of an item
        /// </summary>
        /// <param name="InValue">The value to parse</param>
        /// <returns></returns>
        public enumOrientations ParseOrientation(String InValue)
        {
            if (InValue == "V" || InValue == "Vertical")
                return enumOrientations.Vertical;
            else if (InValue == "H" || InValue == "Horizontal")
                return enumOrientations.Horizontal;
            else if (InValue == "U" || InValue == "Up")
                return enumOrientations.Up;
            else if (InValue == "D" || InValue == "Down")
                return enumOrientations.Down;
            else if (InValue == "L" || InValue == "Left")
                return enumOrientations.Left;
            else if (InValue == "R" || InValue == "Right")
                return enumOrientations.Right;
            else
                return enumOrientations.Unknown;
        }
        #endregion       

        #region Element Creation
        /// <summary>
        /// Initialize a new one-line element from a one-line XML.
        /// </summary>
        /// <param name="ElemConfig">The one line's XML</param>
        /// <param name="DisplayParameters">The display parameters for the one-line</param>
        /// <param name="DbConnection">The database connection</param>
        /// <param name="DisplayElements">The display elements on the screen</param>        
        /// <param name="OLView">The one-line viewer</param>
        public static MM_OneLine_Element CreateElement(XmlElement ElemConfig, MM_OneLine_Display DisplayParameters, MM_Database DbConnection, Dictionary<MM_Element, MM_OneLine_Element> DisplayElements, MM_OneLine_Viewer OLView)
        {
            if (ElemConfig.HasAttribute("BaseElement") && DbConnection != null)
                DbConnection.LocateElement(Convert.ToUInt64(ElemConfig.Attributes["BaseElement"].Value));
            MM_OneLine_Element OutElement = null;
            if (ElemConfig.Name == "Line")
                OutElement = new MM_OneLine_Line(ElemConfig, DisplayParameters, DbConnection);
            else if (ElemConfig.Name == "Breaker")
                OutElement = new MM_OneLine_Breaker(ElemConfig, DisplayParameters, DbConnection);
            else if (ElemConfig.Name == "Switch")
                OutElement = new MM_OneLine_Switch(ElemConfig, DisplayParameters, DbConnection);            
            else if (ElemConfig.Name == "Node")
                OutElement = new MM_OneLine_Node(ElemConfig, DisplayParameters, DbConnection, DisplayElements, OLView);
            else if (ElemConfig.Name == "EPSMeter")
                OutElement = new MM_OneLine_EPSMeter(ElemConfig, DisplayParameters, DbConnection);
            else if (ElemConfig.Name == "Unit")
                OutElement = new MM_OneLine_Unit(ElemConfig, DisplayParameters, DbConnection);
            else if (ElemConfig.Name == "CustomerLoad" || ElemConfig.Name == "Load")
                OutElement = new MM_OneLine_Load(ElemConfig, DisplayParameters, DbConnection);
            else if (ElemConfig.Name == "LAAR")
                OutElement = new MM_OneLine_LAAR(ElemConfig, DisplayParameters, DbConnection);            
            else if (ElemConfig.Name == "ShuntCompensator" && ElemConfig.Attributes["Type"].Value == "Capacitor")
                OutElement = new MM_OneLine_Capacitor(ElemConfig, DisplayParameters, DbConnection);
            else if (ElemConfig.Name == "Capacitor")
                OutElement = new MM_OneLine_Capacitor(ElemConfig, DisplayParameters, DbConnection);
            else if (ElemConfig.Name == "ShuntCompensator" && ElemConfig.Attributes["Type"].Value == "Reactor")
                OutElement = new MM_OneLine_Reactor(ElemConfig, DisplayParameters, DbConnection);
            else if (ElemConfig.Name == "Reactor")
                OutElement = new MM_OneLine_Reactor(ElemConfig, DisplayParameters, DbConnection);
            else if (ElemConfig.Name == "EndCap")
                OutElement = new MM_OneLine_EndCap(ElemConfig, DisplayParameters, DbConnection);
            else if (ElemConfig.Name == "Transformer")
                OutElement = new MM_OneLine_Transformer(ElemConfig, DisplayParameters, DbConnection);
            else if (ElemConfig.Name == "StaticVarCompensator")
                OutElement = new MM_OneLine_StaticVarCompensator(ElemConfig, DisplayParameters, DbConnection);
            else if (ElemConfig.Name == "Unlinked_Element")
                OutElement = new MM_OneLine_UnlinkedElement(ElemConfig, DisplayParameters, DbConnection);           
            else
                throw new InvalidOperationException("Unknown element type: " + ElemConfig.Name);                
            return OutElement;

        }

        /// <summary>
        /// Initialize a new one-line element from a one-line XML.
        /// </summary>
        /// <param name="ElemConfig">The one line's XML</param>
        /// <param name="DisplayParameters">The display parameters for the one-line</param>
        /// <param name="DisplayElements">The collection of one-line elements</param>
        /// <param name="Orientation">The orientation of the element</param>
        public static MM_OneLine_Element CreateElement(MM_Element ElemConfig, MM_OneLine_Display DisplayParameters, Dictionary<MM_Element, MM_OneLine_Element> DisplayElements, MM_OneLine_Element.enumOrientations Orientation)
        {
            String ElemType = ElemConfig.ElemType.Name;
            MM_OneLine_Element OutElement;
            if (ElemType == "Line")
                if ((ElemConfig as MM_Line).IsSeriesCompensator)
                    OutElement = new MM_OneLine_Reactor(ElemConfig, DisplayParameters, Orientation);
                else
                    OutElement = new MM_OneLine_Line(ElemConfig, DisplayParameters, Orientation);
            else if (ElemType == "Breaker")
                OutElement = new MM_OneLine_Breaker(ElemConfig, DisplayParameters, Orientation);
            else if (ElemType == "Disconnector" || ElemType == "Switch")
                OutElement = new MM_OneLine_Switch(ElemConfig, DisplayParameters, Orientation);
            else if (ElemType == "Node" || ElemType == "BusbarSection" || ElemType == "ElectricalBus")
                OutElement = new MM_OneLine_Node(ElemConfig, DisplayParameters, Orientation);
            else if (ElemType == "EPSMeter")
                OutElement = new MM_OneLine_EPSMeter(ElemConfig, DisplayParameters, Orientation);
            else if (ElemType.EndsWith("Load", StringComparison.CurrentCultureIgnoreCase))
                OutElement = new MM_OneLine_Load(ElemConfig, DisplayParameters, Orientation);
            else if (ElemType == "LAAR" || ElemType == "LaaR")
                OutElement = new MM_OneLine_LAAR(ElemConfig, DisplayParameters, Orientation);
            else if (ElemType == "Capacitor")
                OutElement = new MM_OneLine_Capacitor(ElemConfig, DisplayParameters, Orientation);
            else if (ElemType == "Reactor")
                OutElement = new MM_OneLine_Reactor(ElemConfig, DisplayParameters, Orientation);
            else if (ElemType == "Transformer")
                OutElement = new MM_OneLine_Transformer(ElemConfig, DisplayParameters, Orientation);
            else if (ElemType == "EndCap")
                OutElement = new MM_OneLine_EndCap(ElemConfig, DisplayParameters, Orientation);
            else if (ElemType == "SynchronousMachine" || ElemType == "Unit")
                OutElement = new MM_OneLine_Unit(ElemConfig, DisplayParameters, Orientation);
            else if (ElemType == "SVS" || ElemType == "SVC" || ElemType == "StaticVarCompensator")
                OutElement = new MM_OneLine_StaticVarCompensator(ElemConfig, DisplayParameters, Orientation);
            else
                throw new InvalidOperationException("Unknown element type: " + ElemType);

            return OutElement;
        }
        /// <summary>
        /// Convert a series of comma-delimited text into a series of points
        /// </summary>
        /// <param name="PointText"></param>
        /// <returns></returns>
        public static Point[] StringToPoints(String PointText)
        {
            String[] splStr = PointText.Split(',');
            Point[] OutPts = new Point[splStr.Length / 2];
            for (int a = 0; a < splStr.Length; a += 2)
                OutPts[a / 2] = new Point(int.Parse(splStr[a]), int.Parse(splStr[a + 1]));
            return OutPts;
        }

        /// <summary>
        /// Convert a series of integers into a series of points
        /// </summary>
        /// <param name="Points"></param>
        /// <returns></returns>
        public static Point[] IntToPoints(params int[] Points)
        {
            Point[] OutPts = new Point[Points.Length / 2];
            for (int a = 0; a < Points.Length; a += 2)
                OutPts[a / 2] = new Point(Points[a], Points[a + 1]);
            return OutPts;
        }
        #endregion

        /// <summary>
        /// Return a readable string
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            if (BaseElement != null)
                return BaseElement.ToString();
            else
                return this.GetType().Name + " (Unknown)";            
        }


        /// <summary>
        /// The creation parameters for an element, allowing it to have a transparent background
        /// </summary>
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;                                
                cp.ExStyle |= 0x00000020;                
                return cp;
            }
        }

      
        
        /*/// <summary>
        /// Handle the message of the user click, to prevent the scroll into focus.
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {                   
            //If this item receives a WM_SETFOCUS (0x7) event, ignore it.
            /*if (m.HWnd == this.Handle && m.Msg == 7)
                return;
            else* /
            try
            {
                base.WndProc(ref m);
            }
            catch (Exception)
            { }
       }*/


        /// <summary>
        /// Handle the paint event of the background by doing nothing.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaintBackground(PaintEventArgs e)
        {

        }


        /// <summary>
        /// Draw the appropriate image for this element
        /// </summary>
        /// <param name="g"></param>
        public abstract void DrawImage(Graphics g);

        /// <summary>
        /// Handle the painting of the display
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {            
            //Pass the XML component to its native drawing functionality in DDL import mode

            if (DisplayParameters.DDLImportMode && this.Tag != null && this.Tag.GetType().Name == "MM_OneLine_XmlComponent")
                this.Tag.GetType().GetMethod("DrawComponent").Invoke(this.Tag, new object[] { e, this });
            else if (DisplayParameters.DDLImportMode && BaseElement != null)
                e.Graphics.FillRectangle(Brushes.DarkGreen, DisplayRectangle);
            else if (Visible)
                DrawImage(e.Graphics);
        }


        /// <summary>
        /// Write the element's XML into a readable format
        /// </summary>
        /// <param name="xW"></param>
        public void WriteXml(XmlTextWriter xW)
        {
            Object inValue;
            bool NeedsUpdate = false;
            xW.WriteStartElement(this.GetType().Name.Substring(this.GetType().Name.LastIndexOf('_') + 1));

            //First, write our base element
            MM_Serializable.WriteAttributes(xW, BaseElement, "BaseElement");
            foreach (PropertyInfo pI in this.GetType().GetProperties())
                if ((inValue = pI.GetValue(this, null)) == null)
                    NeedsUpdate = false;
                else if (!pI.CanWrite)
                    NeedsUpdate = false;
                else if (pI.Name == "BaseElement")
                    NeedsUpdate = false;
                else if (inValue is MM_Element && (this is MM_OneLine_PokePoint == false || this is MM_OneLine_PricingVector))
                    MM_Serializable.WriteAttributes(xW, inValue, pI.Name);
                else if (pI.Name == "Font" && this is MM_OneLine_Descriptor)
                    MM_Serializable.WriteXMLData(pI.Name, inValue, xW);
                else if (pI.Name == "Descriptor" || pI.Name == "SecondaryDescriptor")
                    NeedsUpdate = false;
                else if (pI.Name == "Windings")
                    NeedsUpdate = false;
                else if (inValue is String && (inValue as String) == "")
                    NeedsUpdate = false;
                else if (pI.DeclaringType == typeof(MM_OneLine_Element) || pI.DeclaringType == this.GetType())
                    MM_Serializable.WriteXMLData(pI.Name, inValue, xW);
                else if (pI.Name == "Font" && this is MM_OneLine_Descriptor)
                    MM_Serializable.WriteXMLData(pI.Name, inValue, xW);
                else if (pI.Name == "Bounds")
                    MM_Serializable.WriteXMLData(pI.Name, inValue, xW);
                else if (pI.Name == "ForeColor" && (Parent == null || !inValue.Equals(Parent.ForeColor)))
                    MM_Serializable.WriteXMLData(pI.Name, inValue, xW);
                else if (pI.Name == "BackColor" && (Parent == null || !inValue.Equals(Parent.BackColor)))
                    MM_Serializable.WriteXMLData(pI.Name, inValue, xW);


            if (NeedsUpdate)
            { }

            //Now, write the descriptors if we have one
            if (Descriptor != null)
                Descriptor.WriteXml(xW);
            if (SecondaryDescriptor != null)
                SecondaryDescriptor.WriteXml(xW);

            //Write out node connection points if possible
            if (this is MM_OneLine_Node)
                foreach (KeyValuePair<MM_OneLine_Element, MM_OneLine_PokePoint[]> kvp in (this as MM_OneLine_Node).ConnectionPoints)
                {
                    xW.WriteStartElement(kvp.Key.GetType().Name.Substring(kvp.Key.GetType().Name.LastIndexOf('_') + 1));
                    MM_Serializable.WriteXMLData("TEID", kvp.Key.TEID, xW);
                    foreach (MM_OneLine_PokePoint Poke in kvp.Value)
                        if (Poke != this)
                            Poke.WriteXml(xW);
                    xW.WriteEndElement();
                }


            if (this is MM_OneLine_Transformer)
                foreach (MM_OneLine_TransformerWinding Winding in (this as MM_OneLine_Transformer).Windings)
                    Winding.WriteXml(xW);

            xW.WriteEndElement();
        }

        #region Drag/drop handling
        /// <summary>
        /// Handle an element being dragged into the area
        /// </summary>
        /// <param name="drgevent"></param>
        protected override void OnDragEnter(DragEventArgs drgevent)
        {
            
            Object InObject = drgevent.Data.GetData(drgevent.Data.GetFormats()[0]);
            MM_Element InElement = null;
            String ElemType = this.GetType().Name.Substring(this.GetType().Name.LastIndexOf('_')+1);
            if (InObject is MM_Element[] && (InObject as MM_Element[]).Length == 1)
                InElement = (InObject as MM_Element[])[0];
            if ((DisplayParameters.EditorMode || DisplayParameters.DDLImportMode) && InElement != null && (InElement.ElemType.Name.EndsWith(ElemType) || (this is MM_OneLine_Transformer && InElement.ElemType.Name == "TransformerWinding") || (this is MM_OneLine_Reactor && InElement is MM_Line && (InElement as MM_Line).IsSeriesCompensator)))
                drgevent.Effect = DragDropEffects.All;
            else
                drgevent.Effect = DragDropEffects.None;


            Point Offset = (Parent as Panel).AutoScrollPosition;
            base.OnMouseMove(new MouseEventArgs(MouseButtons.Left, 0, 1,1, 0));
                
        }

        /// <summary>
        /// Handle an element being dropped into the area
        /// </summary>
        /// <param name="drgevent"></param>
        protected override void OnDragDrop(DragEventArgs drgevent)
        {
            Object InObject = drgevent.Data.GetData(drgevent.Data.GetFormats()[0]);
            MM_Element InElement = null;
            String ElemType = this.GetType().Name.Substring(this.GetType().Name.LastIndexOf('_') + 1);
            try
            {
                if (InObject is MM_Element[] && (InObject as MM_Element[]).Length == 1)
                    InElement = (InObject as MM_Element[])[0];
                if ((DisplayParameters.EditorMode || DisplayParameters.DDLImportMode) && InElement != null && (InElement.ElemType.Name.EndsWith(ElemType)  || (this is MM_OneLine_Transformer && InElement.ElemType.Name == "TransformerWinding") || (this is MM_OneLine_Reactor && InElement is MM_Line && (InElement as MM_Line).IsSeriesCompensator)))
                {
                    if (this.BaseElement != null)
                    {
                        (this.Parent.Parent as MM_OneLine_Viewer).UnassignElement(this);
                        // DisplayParameters.HandleRemovedElements(new MM_OneLine_Element[] { this });
                    }

                    if (InElement.ElemType.Name == "TransformerWinding")
                    {

                        bool FoundWinding = false;
                        MM_OneLine_Transformer XF = this as MM_OneLine_Transformer;
                        for (int a = 0; a < (this as MM_OneLine_Transformer).Windings.Length; a++)
                        {

                            if (!FoundWinding && XF.Windings[a] == null && a == 0)
                            {
                                XF.Windings[a] = new MM_OneLine_TransformerWinding(InElement, new Rectangle(0, 0, Width, Height / 2), enumOrientations.Up);
                                FoundWinding = true;
                            }
                            else if (!FoundWinding && XF.Windings[a] == null && a == 1)
                            {
                                XF.Windings[a] = new MM_OneLine_TransformerWinding(InElement, new Rectangle(0, Height / 2, Width, Height / 2), enumOrientations.Down);
                                if ((BaseElement as MM_Transformer).PhaseShifter)
                                    XF.Windings[a].IsPhaseShifter = true;
                                FoundWinding = true;
                            }
                            else if (!FoundWinding && (XF.Windings[a].BaseElement == InElement || XF.Windings[a].BaseElement == null))
                            {
                                XF.Windings[a].BaseElement = InElement;
                                FoundWinding = true;
                            }
                        }
                        if (!FoundWinding)
                        {
                            DialogResult Resp = MessageBox.Show(String.Format("Would you like to roll {0} {1} {2}\n  into {3} {4} ({5}; answer yes),\n  or {6} {7} ({8}; answer no)?", InElement.KVLevel.Name, InElement.ElemType.Name, InElement.Name, XF.Windings[0].BaseElement.KVLevel.Name, XF.Windings[0].BaseElement.Name, XF.Windings[0].Orientation,XF.Windings[1].BaseElement.KVLevel.Name, XF.Windings[1].BaseElement.Name, XF.Windings[1].Orientation), Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                            if (Resp == DialogResult.Yes)
                                XF.Windings[0].BaseElement = InElement;
                            else if (Resp == DialogResult.No)
                                XF.Windings[1].BaseElement = InElement;
                        }
                    }
                    else
                        this.BaseElement = InElement;

                    //If we have a busbar section, let's handle things accordingly.
                    if (this is MM_OneLine_Node)                    
                        DisplayParameters.HandleAddedNodes(new MM_OneLine_Node[] { this as MM_OneLine_Node });
                    

                    this.Refresh();
                    if (Descriptor != null)
                    {
                        Descriptor.Invalidate();
                        Descriptor.RecomputeSize();
                    }
                    else
                    {
                        Descriptor = new MM_OneLine_Descriptor(this, DisplayParameters);
                        Descriptor.Bounds = new Rectangle(this.Left, this.Bottom + 2, 60, 30);
                        DisplayParameters.HandleAddedElements(new MM_OneLine_Element[] { Descriptor });
                    }

                    DisplayParameters.HandleAssignedElements(new MM_OneLine_Element[] { this });
                    DisplayParameters.HandleAddedElements(new MM_OneLine_Element[] { this });
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error on drag drop: " + ex.Message + "\n" + ex.StackTrace, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        #endregion     
    
    }
}
